#include "uart.h"

volatile uint32_t errors = 0;

uint8_t rx_buf[FRAME_DATA_SIZE];   // double buffer on incoming data
uint8_t tx_buf[FRAME_SIZE];        // a full frame to send out
uint8_t data_buf[FRAME_DATA_SIZE]; // Contains most recent data sent to us
volatile uint16_t data_num  = 0; // Incremented whenever valid new data received
volatile uint8_t data_ready = 0; // True when valid data sitting in data buffer

volatile uint16_t rx_hd = 0,
                  rx_tl = 0;
volatile uint8_t rx_idx = 0;

volatile uint16_t tx_len = 0;
volatile uint16_t tx_cnt = 0;

enum rx_state_t {
    IDLE = 0,                   // Waiting for new frame
    B1   = 2,                   // Begin 1
    E1   = 4,                   // End 1
    DATA = 6                    // Accepting Data
} rx_state; 


//////////////////////////////////////////////////////////////////////
// Updates the protocol state machine based on the most recently received byte
static void proto_state_tick(uint8_t char_in) {
    switch(__even_in_range(rx_state, 8)) {
    case IDLE:
  	if (char_in == SS) {
            rx_state = B1;
  	} else {
            rx_state = IDLE;
  	}
  	break;
  	
    case B1:
  	if (char_in == SS) {
            rx_idx = 0; // first 2 bytes are SS
            rx_state = DATA;
  	} else {
            rx_state = IDLE;
  	}
  	break;
  	
    case DATA:
  	if (rx_idx == FRAME_DATA_SIZE) {
            if (char_in == ES) {
                rx_state = E1;
            } else {
                rx_state = IDLE;
            }
  	} else {
            rx_buf[rx_idx] = char_in;
            rx_idx++;
  	}
  	break;
  	
    case E1:
  	if (char_in == ES) {
            // Copy received data into the data buf
            memcpy(data_buf, rx_buf, FRAME_DATA_SIZE);
            data_num++;
            data_ready = 1;
            rx_state = IDLE;
  	} else {
            rx_state = IDLE;
  	}
  	break;
  	
    default: break;
    }
}

//////////////////////////////////////////////////////////////////////
//void send_uart_data(const uint8_t *buffer, uint16_t length){
void send_uart_data(uint8_t *buffer, uint8_t length){
    // Silently truncate too-long messages
    if (length > FRAME_DATA_SIZE) {
        length = FRAME_DATA_SIZE;
    }
    
    //// OPTION 1 - with TX ISR
    
    // Copy user data into the transmit buffer
    uint8_t i;
    for (i = 0; i < length; i++) {
        tx_buf[i+2] = buffer[i];
    }

    // Zero out the rest
    for (i = length; i < FRAME_DATA_SIZE; i++) {
        tx_buf[i+2] = 0;
    }
    
    tx_cnt = 0;
    tx_len = FRAME_SIZE;
    UCA1IE |= UCTXIE;
    LPM0;

    //// OPTION 2 - using spin loops

    // volatile uint16_t i = 0;
    // while (i < length) {
    //     while (!(UCA1IFG & UCTXIFG)); // wait for line to be ready
    //     UCA1TXBUF = buffer[i];
    //     i++;
    // }
    return;
}

//////////////////////////////////////////////////////////////////////
// Blocks until new data has been received.
uint16_t uart_receive_blocking (uint8_t *buffer) {
    while (!data_ready);        // spin, hopefully not for too long
    return uart_receive(buffer);
}

//////////////////////////////////////////////////////////////////////
// Copies most recently received data into user's buffer and returns the "age"
// of that data.
uint16_t uart_receive (uint8_t *buffer) {
    memcpy(buffer, data_buf, FRAME_DATA_SIZE);
    data_ready = 0;             // indicate we've cleaned out the buffer
    return data_num;
}

//////////////////////////////////////////////////////////////////////
void initialize_UART(){
    P4SEL = BIT4+BIT5;          // P3.4,5 = USCI_A1 TXD/RXD
    UCA1CTL1 |= UCSWRST;        // **Put state machine in reset**
    UCA1CTL1 |= UCSSEL_2;       // SMCLK

    //// 115200 at 12Mhz
    UCA1BR0 = 6;
    UCA1BR1 = 0;
    UCA1MCTL = UCBRS_0 | UCBRF_8 | UCOS16;

    UCA1CTL1 &= ~UCSWRST;       // **Initialize USCI state machine**
    UCA1IE |= UCRXIE;           // Enable USCI_A0 RX interrupt

    // Insert data frame sentinels
    tx_buf[0] = SS;
    tx_buf[1] = SS;
    tx_buf[FRAME_DATA_SIZE + 2] = ES;
    tx_buf[FRAME_DATA_SIZE + 3] = ES;
}

//////////////////////////////////////////////////////////////////////
// UART Interrupt
#ifdef __GNUC__
INTERRUPT(USCI_A1_VECTOR, USCI_A1_ISR)
#else
#pragma vector=USCI_A1_VECTOR
    __interrupt void USCI_A1_ISR(void)
#endif
{
    switch(UCA1IV) {
    case 0:break;               // Vector 0 - no interrupt
    case 2:                     // Vector 2 - RXIFG
        proto_state_tick(UCA1RXBUF);
        break; 
    case 4:                     // Vector 4 - TXIFG
        UCA1TXBUF = tx_buf[tx_cnt++];
        if (tx_cnt == tx_len) {
            UCA1IE &= ~UCTXIE;
            __bic_SR_register_on_exit(LPM0_bits);
        }
        break;              
    default: break;
    }
}
